import 'package:decimal/decimal.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:lib_uikit/widgets/text/gt_text_span.dart';
import '../../generated/l10n.dart';
import 'chart_xy_value_utils.dart';
import 'model/chart_dot_data.dart';
import 'model/chart_multiple_dot_data.dart';
import 'model/chart_xy_data.dart';
import 'package:lib_uikit/lib_uikit.dart';
import 'package:lib_core/lib_core.dart';

/// @Title   折线图组件
/// @Author: bryce
/// @CreateDate:  2023/8/16 15:18
class ChartLineWidget extends StatefulWidget {
  /// 数据描点：1：必须是连续日期，2：日期数量需大于等于四
  final List<ChartDotData> dots;

  /// 第二条折线
  final List<ChartDotData>? dots2;

  /// 第三条折线
  final List<ChartDotData>? dots3;

  /// 折线的单色
  final Color? singleColor;

  /// 第二条折线
  final Color? singleColor2;

  /// 第三条折线
  final Color? singleColor3;

  /// 折线的上下两部分的折线颜色（非填充色，填充色是在此基础上30%透明度）
  final Color? topColor;
  final Color? bottomColor;

  /// 是否展示上下填充色,
  final bool showAboveBellowAreaColor;

  /// 折线图的宽高
  final double height;
  final double? width;

  /// 需要展示的y轴分隔值，例如传了0，则y轴绝对值最小值为0,否则自动根据数据点计算最小值
  final double? cutY;

  /// 加这个边距的原因是防止最后的点被切掉,默认为10
  final double rightPadding;

  /// 触摸图表时弹框内的内容 (只有一条曲线时定义触摸pop框时使用这个参数)
  final LineTooltipItem Function(int index, ChartDotData dots)? buildTooltipItem;

  /// 如果是多条折线的话，回调使用这个来写，指同一个触摸位置多条线的触摸点(多条曲线时定义触摸pop框时使用这个参数)
  final LineTooltipItem Function(int index, String date, ChartDotData? dot1, ChartDotData? dot2, ChartDotData? dot3)?
      buildMultipleTooltipItem;

  /// 根据y值value获取左侧显示的文案，如不传则默认千分位展示
  final String Function(double y)? getLeftTitleByY;

  /// 对时间排序的方法，如果是多折线图的话，必须传
  /// 因为如果第一条线传的日期是1、3、5，第二条线是2、4、6，需要排一下他们的日期
  final List<String> Function(List<String> needSortDateList)? sortDate;

  /// 是否展示最高最低值,以及提示的textPainter
  final bool showMostTopTip;
  final bool showMostBottomTip;
  final TextPainter Function(bool isTop, double value)? mostTopBottomTextPainter;

  /// 是否展示最高最低值-第二条线
  final bool showMostTopTip2;
  final bool showMostBottomTip2;

  /// 是否展示最高最低值-第三条线
  final bool showMostTopTip3;
  final bool showMostBottomTip3;

  /// 折线是否弯曲
  final bool isCurved;
  final bool isCurved2;
  final bool isCurved3;

  /// 是否仅展示x轴的收尾坐标
  final bool onlyShowFirstLastBottomTitle;

  /// 是否在触摸结束时消失tip框
  final bool dismissTouchPopWhenTouchEnd;

  /// 是否允许展示触摸指示线
  final bool enableTouchIndicator;

  /// 每条折线的宽度
  final double barWidth;

  /// 是否允许手势交互
  final bool enableTouch;

  /// 是否展示Y坐标
  final bool showLeftTitle;

  /// 是否展示X坐标
  final bool showBottomTitle;

  /// 是否展示网格线
  final bool showGridLine;

  /// 是否展示底部边框
  final bool showBottomBorder;

  /// 触摸手势的回调，参数isTouchInSpot代表触摸时是否在某个点上
  /// isTouchInSpot为false时包括手势移动到非描点处、手势抬起、手势取消；只有在手势触摸在描点上时才会为true
  final Function(bool isTouchInSpot)? onTouchSpotsListener;

  /// x轴的固定间隔(如果不传会自动计算，传入的话需要注意间隔太小的话会放不下)
  final int? xInterval;

  /// 防止弯曲的时候越界(默认为true,不建议修改，除非有特殊需求需要展示大平滑度)
  final bool preventCurveOverShooting;

  /// 曲线平滑度值，默认0.35，当isCurve为true时才有效
  final double curveSmoothness;

  /// 设置图表行高，设置这个参数时，height 不生效
  final double? chartRowHeight;

  final bool? fitInsideVertically;

  const ChartLineWidget(
      {super.key,
      required this.dots,
      this.dots2,
      this.dots3,
      this.singleColor,
      this.singleColor2,
      this.singleColor3,
      this.isCurved = true,
      this.isCurved2 = true,
      this.isCurved3 = true,
      this.topColor,
      this.bottomColor,
      this.showAboveBellowAreaColor = true,
      this.getLeftTitleByY,
      this.sortDate,
      this.buildTooltipItem,
      this.buildMultipleTooltipItem,
      this.rightPadding = 10,
      this.cutY,
      this.showMostTopTip = false,
      this.showMostBottomTip = false,
      this.mostTopBottomTextPainter,
      this.showMostTopTip2 = false,
      this.showMostBottomTip2 = false,
      this.showMostTopTip3 = false,
      this.showMostBottomTip3 = false,
      this.onlyShowFirstLastBottomTitle = false,
      this.dismissTouchPopWhenTouchEnd = false,
      this.enableTouchIndicator = true,
      this.onTouchSpotsListener,
      this.barWidth = 2.0,
      this.enableTouch = true,
      this.showLeftTitle = true,
      this.showBottomTitle = true,
      this.showGridLine = true,
      this.showBottomBorder = true,
      this.xInterval,
      this.preventCurveOverShooting = true,
      this.curveSmoothness = 0.35,
      this.width,
      this.height = 300,
      this.chartRowHeight,
      this.fitInsideVertically})
      : assert((topColor != null && bottomColor != null) || singleColor != null, 'color must not null'),
        assert(dots2 == null || sortDate != null, '设置了dots2必须同时设置sortDate'),
        assert(dots2 == null || singleColor != null, '设置了dots2必须同时设置singleColor'),
        assert(dots3 == null || dots2 != null, '设置了dots3必须同时设置dots2');

  @override
  State<StatefulWidget> createState() => _ChartLineWidgetState();
}

class _ChartLineWidgetState extends State<ChartLineWidget> {
  /// 多曲线的整理的数据
  List<ChartMultipleDotData>? multipleDots;
  Map<String, ChartMultipleDotData>? multipleLineDataMap;

  // 是否只有一条折线
  bool get isSingleLine => null == multipleDots || multipleDots!.isEmpty;

  // XY相关数据
  late ChartXYData chartXYData;

  bool get isAllAboveZero => chartXYData.minValueYReal >= 0;

  bool get isAllBellowZero => chartXYData.maxValueYReal <= 0;

  @override
  void initState() {
    super.initState();
    // 处理多条曲线数据集
    _handleMultipleDotData();
    if (!isSingleLine) {
      // 初始化计算XY相关数据
      chartXYData = ChartXYValueUtils.calculateChartXYDataByMultiplierDots(multipleDots!,
          cutY: widget.cutY, isCurvedLine: true, xInterval: widget.xInterval);
    } else {
      // 初始化计算XY相关数据
      chartXYData = ChartXYValueUtils.calculateChartXYData(widget.dots,
          cutY: widget.cutY, isCurvedLine: true, xInterval: widget.xInterval);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      // 加这个边距的原因是防止最后的点被切掉
      padding: GTEdgeInsetsOnly(right: widget.rightPadding),
      child: SizedBox(
          height: widget.chartRowHeight != null
              ? widget.chartRowHeight! * (chartXYData.maxYChart - chartXYData.minYChart) / chartXYData.yIntervalChart +
                  30
              : widget.height,
          width: widget.width,
          child: LineChart(
            lineChatData(),
          )),
    );
  }

  LineChartData lineChatData() {
    TextStyle leftTitleStyle = gtTextStyleW400(color: GTColorsV3.text3.color, fontSize: 10);
    int maxLength = 0;
    String maxLengthValueStr = '';
    for (double i = chartXYData.minYChart; i <= chartXYData.maxYChart; i += chartXYData.yIntervalChart) {
      String tempStr = _getLeftTitleByY(i);
      if (tempStr.length > maxLength) {
        maxLength = tempStr.length;
        maxLengthValueStr = tempStr;
      }
    }
    double leftMaxWidth = TextUtils.calculateTextSize(context, '$maxLengthValueStr  ', textStyle: leftTitleStyle).width;

    AxisTitles? chartLeftTitles = AxisTitles(
      sideTitles: SideTitles(
        showTitles: widget.showLeftTitle,
        interval: chartXYData.yIntervalChart,
        getTitlesWidget: (value, meta) => leftTitleWidgets(value, meta, leftTitleStyle),
        reservedSize: leftMaxWidth + 5,
      ),
    );

    return LineChartData(
      // 触摸时的气泡和指示器
      lineTouchData: lineTouchData,
      // 中间的网格
      gridData: gridData,
      // 最大最小坐标，不设置的话按照数据最大最小来
      minX: chartXYData.minX,
      maxX: chartXYData.maxX,
      minY: chartXYData.minYChart,
      maxY: chartXYData.maxYChart,
      titlesData: FlTitlesData(
        show: true,
        rightTitles:
            GTThemeProvider.instance.isRTL() ? chartLeftTitles : AxisTitles(sideTitles: SideTitles(showTitles: false)),
        topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
        bottomTitles: AxisTitles(
          sideTitles: SideTitles(
            showTitles: widget.showBottomTitle,
            reservedSize: 30,
            interval: 1,
            getTitlesWidget: bottomTitleWidgets,
          ),
        ),
        leftTitles:
            GTThemeProvider.instance.isRTL() ? AxisTitles(sideTitles: SideTitles(showTitles: false)) : chartLeftTitles,
      ),
      // 边框
      borderData: borderData,
      // 折线数据，包括数据、颜色、填充、样式
      lineBarsData: [
        chartBarItem(widget.dots,
            singleLineColor: widget.singleColor,
            isCurved: widget.isCurved,
            showMostTopTip: widget.showMostTopTip,
            showMostBottomTip: widget.showMostBottomTip),
        if (widget.dots2 != null)
          chartBarItem(widget.dots2!,
              singleLineColor: widget.singleColor2 ?? widget.singleColor,
              isCurved: widget.isCurved2,
              showMostTopTip: widget.showMostTopTip2,
              showMostBottomTip: widget.showMostBottomTip2),
        if (widget.dots3 != null)
          chartBarItem(widget.dots3!,
              singleLineColor: widget.singleColor3 ?? widget.singleColor,
              isCurved: widget.isCurved3,
              showMostTopTip: widget.showMostTopTip3,
              showMostBottomTip: widget.showMostBottomTip3),
      ],
    );
  }

  LineChartBarData chartBarItem(List<ChartDotData> itemDots,
      {Color? singleLineColor, bool isCurved = true, bool showMostTopTip = false, bool showMostBottomTip = false}) {
    List<FlSpot> spots = [];
    if (isSingleLine) {
      spots = itemDots
          .asMap()
          .entries
          .map((e) => FlSpot(e.key.toDouble(), GTNumberFormat.getNumMultiply(e.value.value, chartXYData.yMultiplier)))
          .toList();
    } else {
      for (var element in itemDots) {
        ChartMultipleDotData? multipleDotData = multipleLineDataMap?[element.date];
        if (null != multipleDotData && (multipleDotData.xIndex ?? -1) >= 0) {
          spots.add(FlSpot(multipleDotData.xIndex!.toDouble(),
              GTNumberFormat.getNumMultiply(element.value, chartXYData.yMultiplier)));
        }
      }
    }
    return LineChartBarData(
        showMostTopTip: showMostTopTip,
        showMostBottomTip: showMostBottomTip,
        mostTopBottomTextPainter: (isTop, value) {
          value = GTNumberFormat.getNumDivision(value, chartXYData.yMultiplier);
          if (null != widget.mostTopBottomTextPainter) {
            return widget.mostTopBottomTextPainter!.call(isTop, value);
          } else {
            return TextUtils.calculateTextSizeForPainter(context, GTNumberFormat.formatNumberThousands(value),
                textStyle: TextStyle(
                    color: singleLineColor ?? (value > 0 ? widget.topColor : widget.bottomColor), fontSize: 10));
          }
        },
        spots: spots,
        // 是否弯曲
        isCurved: isCurved,
        curveSmoothness: widget.curveSmoothness,
        // 防止弯曲的时候越界
        preventCurveOverShooting: widget.preventCurveOverShooting,
        color: singleLineColor ??
            (isAllAboveZero
                ? widget.topColor
                : isAllBellowZero
                    ? widget.bottomColor
                    : null),
        // 折线颜色
        gradient: (null != singleLineColor || isAllAboveZero || isAllBellowZero)
            ? null
            : LinearGradient(colors: [
                widget.topColor!,
                widget.topColor!,
                widget.bottomColor!,
                widget.bottomColor!
              ], stops: [
                0,
                chartXYData.maxValueYChart / (chartXYData.maxValueYChart - chartXYData.minValueYChart) - 0.01,
                chartXYData.maxValueYChart / (chartXYData.maxValueYChart - chartXYData.minValueYChart) + 0.01,
                1
              ], begin: Alignment.topCenter, end: Alignment.bottomCenter),

        // 折线上下填充色
        // 折线下方区域填充（即0以上区域）
        belowBarData: !widget.showAboveBellowAreaColor || null != singleLineColor || isAllBellowZero
            ? null
            : BarAreaData(
                show: true,
                gradient: isAllAboveZero
                    ? LinearGradient(colors: [
                        widget.topColor!.withOpacity(0.3),
                        widget.topColor!.withOpacity(0),
                      ], begin: Alignment.topCenter, end: Alignment.bottomCenter)
                    : LinearGradient(colors: [
                        widget.topColor!.withOpacity(0.3),
                        widget.topColor!.withOpacity(0),
                        widget.topColor!.withOpacity(0),
                      ], stops: [
                        0,
                        chartXYData.maxValueYChart / (chartXYData.maxValueYChart - chartXYData.minValueYChart),
                        1
                      ], begin: Alignment.topCenter, end: Alignment.bottomCenter),
                cutOffY: 0,
                applyCutOffY: true),
        // 折线上方区域填充（即0以下区域）
        aboveBarData: !widget.showAboveBellowAreaColor || null != singleLineColor || isAllAboveZero
            ? null
            : BarAreaData(
                show: true,
                gradient: isAllBellowZero
                    ? LinearGradient(colors: [
                        widget.bottomColor!.withOpacity(0.3),
                        widget.bottomColor!.withOpacity(0),
                      ], begin: Alignment.bottomCenter, end: Alignment.topCenter)
                    : LinearGradient(
                        colors: [
                          widget.bottomColor!.withOpacity(0.3),
                          widget.bottomColor!.withOpacity(0),
                          widget.bottomColor!.withOpacity(0)
                        ],
                        stops: [
                          0,
                          chartXYData.minValueYChart.abs() / (chartXYData.maxValueYChart - chartXYData.minValueYChart),
                          1
                        ],
                        begin: Alignment.bottomCenter,
                        end: Alignment.topCenter,
                      ),
                cutOffY: 0,
                applyCutOffY: true),
        barWidth: widget.barWidth,
        isStrokeCapRound: true,
        dotData: const FlDotData(show: false));
  }

  FlGridData get gridData => FlGridData(
        show: widget.showGridLine,
        horizontalInterval: chartXYData.yIntervalChart,
        drawVerticalLine: false,
        getDrawingHorizontalLine: (double value) {
          return FlLine(
            color: GTColorsV3.divider1.color,
            strokeWidth: 1,
          );
        },
      );

  FlBorderData get borderData => FlBorderData(
        show: widget.showBottomBorder,
        border: Border(bottom: BorderSide(color: GTColorsV3.divider1.color, width: 1)),
      );

  Widget bottomTitleWidgets(double value, TitleMeta meta) {
    int currentIndex = value.toInt();

    bool showTitle = false;
    if (widget.onlyShowFirstLastBottomTitle) {
      if (currentIndex == 0 || currentIndex == chartXYData.maxX) {
        showTitle = true;
      } else {
        showTitle = false;
      }
    } else {
      if (chartXYData.maxX < 4 || (chartXYData.maxX - currentIndex) % chartXYData.xInterval == 0) {
        showTitle = true;
      } else {
        showTitle = false;
      }
    }

    if (showTitle) {
      TextStyle style = gtTextStyleW400(color: GTColorsV3.text3.color, fontSize: 10);
      return SideTitleWidget(
        axisSide: meta.axisSide,
        fitInside: SideTitleFitInsideData.fromTitleMeta(meta),
        child: Text(null != multipleDots ? multipleDots![currentIndex].date : widget.dots[value.toInt()].date ?? '',
            style: style),
      );
    } else {
      return Container();
    }
  }

  Widget leftTitleWidgets(double value, TitleMeta meta, TextStyle leftTitleStyle) {
    Decimal result = Decimal.parse('$value') % Decimal.parse('${chartXYData.yIntervalChart}');
    if (result.toDouble() == 0) {
      return SideTitleWidget(
        axisSide: meta.axisSide,
        space: 4,
        child: Text(
          '${GTThemeProvider.instance.isRTL() ? '  ' : ''}${_getLeftTitleByY(GTNumberFormat.getNumDivision(value, chartXYData.yMultiplier))}${GTThemeProvider.instance.isRTL() ? '' : '  '}',
          style: leftTitleStyle,
          textDirection: TextDirection.ltr,
          textAlign: TextAlign.center,
        ),
      );
    } else {
      return Container();
    }
  }

  LineTouchData get lineTouchData => LineTouchData(
        enabled: widget.enableTouch,
        touchCallback: (FlTouchEvent event, LineTouchResponse? touchResponse) {
          if (!event.isInterestedForInteractions ||
              touchResponse?.lineBarSpots == null ||
              touchResponse!.lineBarSpots!.isEmpty) {
            // 不显示Tooltips
            widget.onTouchSpotsListener?.call(false);
          } else {
            widget.onTouchSpotsListener?.call(true);
          }
        },
        // 是否在触摸结束时消失tip框
        dismissTouchPopWhenTouchEnd: widget.dismissTouchPopWhenTouchEnd,
        // 是否允许触摸
        handleBuiltInTouches: widget.enableTouchIndicator,
        // 圆点指示器设置(触摸时的虚线和圆点)
        getTouchedSpotIndicator: (LineChartBarData barData, List<int> spotIndexes) {
          return spotIndexes.map((spotIndex) {
            Color dotStrokeColor;
            if (null != widget.singleColor) {
              dotStrokeColor = widget.singleColor!;
            } else {
              dotStrokeColor = widget.topColor!;
              if (isAllAboveZero) {
                dotStrokeColor = widget.topColor!;
              } else if (isAllBellowZero) {
                dotStrokeColor = widget.bottomColor!;
              } else {
                double dotY = spotIndex < barData.spots.length ? barData.spots[spotIndex].y : 0;
                dotStrokeColor = (dotY >= 0 ? widget.topColor! : widget.bottomColor!);
              }
            }
            return TouchedSpotIndicatorData(
              FlLine(color: GTColorsV3.divider4.color, strokeWidth: 1, dashArray: [6, 4]),
              FlDotData(
                getDotPainter: (spot, percent, barData, index) =>
                    FlDotCirclePainter(radius: 3, strokeWidth: 0, color: barData.color ?? dotStrokeColor),
              ),
            );
          }).toList();
        },
        touchTooltipData: LineTouchTooltipData(
          tooltipBgColor: GTColorsV3.uikitCmpt6V3.color,
          // tooltipBorder: BorderSide(),
          fitInsideHorizontally: true,
          fitInsideVertically: widget.fitInsideVertically ?? true,
          tooltipPadding: GTEdgeInsetsFormRTLB(12, 4, 12, 8),
          getTooltipItems: (List<LineBarSpot> touchedBarSpots) {
            if (touchedBarSpots.isEmpty) return [];
            // 多折线
            if (null != widget.dots2) {
              int pointIndex = 0;
              String date = '';
              LineBarSpot? flSpot1;
              LineBarSpot? flSpot2;
              LineBarSpot? flSpot3;
              ChartDotData? dot1;
              ChartDotData? dot2;
              ChartDotData? dot3;
              for (var element in touchedBarSpots) {
                if (element.barIndex == 0) {
                  flSpot1 = element;
                  pointIndex = flSpot1.x.toInt();
                  dot1 = widget.dots[pointIndex];
                  date = dot1.date;
                } else if (element.barIndex == 1) {
                  flSpot2 = element;
                  pointIndex = flSpot2.x.toInt();
                  dot2 = widget.dots2![pointIndex];
                  date = dot2.date;
                } else if (element.barIndex == 2) {
                  flSpot3 = element;
                  pointIndex = flSpot3.x.toInt();
                  dot3 = widget.dots3![pointIndex];
                  date = dot3.date;
                }
              }
              if (null == dot1 && null == dot2 && null == dot3) {
                return [];
              }
              return [
                widget.buildMultipleTooltipItem?.call(pointIndex, date, dot1, dot2, dot3) ??
                    _defaultMultipleTooltipItem(date, dot1, dot2, dot3)
              ];
            } else {
              final flSpot = touchedBarSpots[0];
              int pointIndex = flSpot.x.toInt();
              int barIndex = flSpot.barIndex;
              ChartDotData dot = widget.dots[pointIndex];
              return [widget.buildTooltipItem?.call(pointIndex, dot) ?? _defaultTooltipItem(dot)];
            }
          },
        ),
      );

  /// 默认单折线的触摸弹框
  LineTooltipItem _defaultTooltipItem(ChartDotData dot) {
    return LineTooltipItem(
      '${dot.date} \n',
      gtTextStyleW500(color: GTColorsV3.uikitText2V3.dark, fontSize: 12).copyWith(height: 1 + 8 / 12),
      textAlign: TextAlign.start,
      textDirection: GTThemeProvider.instance.isRTL() ? TextDirection.rtl : TextDirection.ltr,
      children: [
        GTTextSpan(text: '${LibUIKitS.current.total_return_profit} '),
        GTTextSpan(
          text: GTNumberFormat.formatNumberKM(dot.value),
          style: gtTextStyleW500(color: GTColorsV3.uikitText6V3.color, fontSize: 12),
        )
      ],
    );
  }

  /// 默认多折线的触摸弹框
  LineTooltipItem _defaultMultipleTooltipItem(String date, ChartDotData? dot1, ChartDotData? dot2, ChartDotData? dot3) {
    return LineTooltipItem(
      '$date \n',
      gtTextStyleW500(color: GTColorsV3.uikitText2V3.dark, fontSize: 12).copyWith(height: 1 + 8 / 12),
      textAlign: TextAlign.start,
      textDirection: GTThemeProvider.instance.isRTL() ? TextDirection.rtl : TextDirection.ltr,
      children: [
        if (null != dot1) GTTextSpan(text: '${LibUIKitS.current.total_return_profit} '),
        if (null != dot1)
          GTTextSpan(
            text: GTNumberFormat.formatNumberKM(dot1.value),
            style: gtTextStyleW500(color: GTColorsV3.uikitText6V3.color, fontSize: 12),
          ),
        if (null != dot2 || null != dot3) GTTextSpan(text: '\n'),
        if (null != dot2) GTTextSpan(text: '${LibUIKitS.current.return_profit} '),
        if (null != dot2)
          GTTextSpan(
            text: GTNumberFormat.formatNumberKM(dot2.value),
            style: gtTextStyleW500(color: GTColorsV3.uikitText6V3.color, fontSize: 12),
          ),
        if (null != dot3) GTTextSpan(text: '\n'),
        if (null != dot3) GTTextSpan(text: '${LibUIKitS.current.total_return_profit} '),
        if (null != dot3)
          GTTextSpan(
            text: GTNumberFormat.formatNumberKM(dot3.value),
            style: gtTextStyleW500(color: GTColorsV3.uikitText6V3.color, fontSize: 12),
          )
      ],
    );
  }

  String _getLeftTitleByY(double value) {
    return null != widget.getLeftTitleByY
        ? widget.getLeftTitleByY!(value)
        : GTNumberFormat.formatNumberThousands(value, maxFractionDigits: 10);
  }

  /// 处理多条曲线数据集
  _handleMultipleDotData() {
    if (null != widget.dots2 && widget.dots2!.isNotEmpty) {
      multipleLineDataMap = {};
      for (var element in widget.dots) {
        ChartMultipleDotData multipleDotData = ChartMultipleDotData(element.date, value1: element.value);
        multipleLineDataMap![element.date] = multipleDotData;
      }
      for (var element in widget.dots2!) {
        ChartMultipleDotData? multipleDotData = multipleLineDataMap![element.date];
        if (null != multipleDotData) {
          multipleDotData.value2 = element.value;
        } else {
          multipleDotData = ChartMultipleDotData(element.date, value2: element.value);
        }
        multipleLineDataMap![element.date] = multipleDotData;
      }

      if (null != widget.dots3 && widget.dots3!.isNotEmpty) {
        for (var element in widget.dots3!) {
          ChartMultipleDotData? multipleDotData = multipleLineDataMap![element.date];
          if (null != multipleDotData) {
            multipleDotData.value3 = element.value;
          } else {
            multipleDotData = ChartMultipleDotData(element.date, value3: element.value);
          }
          multipleLineDataMap![element.date] = multipleDotData;
        }
      }

      multipleDots = [];
      List<String> sortedDateList = widget.sortDate!.call(multipleLineDataMap!.keys.toList());
      for (int i = 0; i < sortedDateList.length; i++) {
        ChartMultipleDotData? multipleDotData = multipleLineDataMap![sortedDateList[i]];
        multipleDotData?.xIndex = i;
        multipleDots!.add(multipleDotData!);
      }
    }
  }
}
